#include "Exception.h"
#include "FileStream.h"

using namespace Woo::IO;

FileStream::FileStream(string path) :
	m_path(path),
	m_fd(-1),
	m_hasClosed(false)
{
	m_fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO);
	if(m_fd == -1)
		m_hasClosed = true;
}

FileStream::FileStream(string path, int flags) :
	m_path(path),
	m_fd(-1),
	m_hasClosed(false)
{
	m_fd = open(path.c_str(), flags, S_IRWXU|S_IRWXG|S_IRWXO);
	if(m_fd == -1)
		m_hasClosed = true;
}

FileStream::FileStream(string path, int flags, mode_t mode) :
	m_path(path),
	m_fd(-1),
	m_hasClosed(false)
{
	m_fd = open(path.c_str(), flags, mode);
	if(m_fd == -1)
		m_hasClosed = true;
}

FileStream::~FileStream()
{
	if(m_fd != -1 && !m_hasClosed)
		Close();
}

UInt32 FileStream::GetLength()
{
	struct stat buf;
	if(stat(m_path.c_str(), &buf) == -1)
	{
		throw StreamException(strerror(errno), __FILE__, __LINE__);
	}
	return buf.st_size;
}

UInt32 FileStream::GetPosition()
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		throw StreamClosedException("stream has closed.", __FILE__, __LINE__);
	}
	off_t pos = lseek(m_fd, 0, SEEK_CUR);
	if(pos == (off_t)-1)
	{
		throw StreamException(strerror(errno), __FILE__, __LINE__);
	}
	return pos;
}

void FileStream::SetPosition(UInt32 position)
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		throw StreamClosedException("stream has closed.", __FILE__, __LINE__);
	}
	off_t pos = lseek(m_fd, position, SEEK_SET);
	if(pos == (off_t)-1)
	{
		throw StreamException(strerror(errno), __FILE__, __LINE__);
	}
}

UInt32 FileStream::Write(const Byte* buffer, UInt32 bufferSize)
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		throw StreamClosedException("stream has closed.", __FILE__, __LINE__);
	}
	ssize_t wc = write(m_fd, buffer, bufferSize);
	if(wc == -1)
	{
		throw StreamWriteException("stream write fail.", __FILE__, __LINE__);
	}
	return wc;
}

UInt32 FileStream::Read(Byte* buffer, UInt32 bufferSize)
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		throw StreamClosedException("stream has closed.", __FILE__, __LINE__);
	}
	ssize_t rc = read(m_fd, buffer, bufferSize);
	if(rc == -1)
	{
		throw StreamReadException("stream read fail.", __FILE__, __LINE__);
	}
	return rc;
}

void FileStream::Close()
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		return;
	}
	close(m_fd);
	m_hasClosed = true;
}

bool FileStream::IsClosed()
{
	return m_hasClosed;
}

bool FileStream::CanRead()
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		return false;
	}
	fd_set rfds;
	struct timeval tv;
	memset(&tv, 0, sizeof(timeval));

	FD_ZERO(&rfds);
	FD_SET(m_fd, &rfds);

	return select(m_fd+1, &rfds, 0, 0, &tv) <= 0 ? false : true;
}

bool FileStream::CanWrite()
{
	if(m_fd == -1)
	{
		throw StreamOpenException("file stream create or open fail.", __FILE__, __LINE__);
	}
	if(m_hasClosed)
	{
		return false;
	}
	fd_set wfds;
	struct timeval tv;
	memset(&tv, 0, sizeof(timeval));

	FD_ZERO(&wfds);
	FD_SET(m_fd, &wfds);

	return select(m_fd+1, 0, &wfds, 0, &tv) <= 0 ? false : true;
}
